Package g4p_controls

Source Code of g4p_controls.GStick

/*
  Part of the GUI for Processing library
    http://www.lagers.org.uk/g4p/index.html
  http://gui4processing.googlecode.com/svn/trunk/

  Copyright (c) 2008-12 Peter Lager

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

package g4p_controls;

import g4p_controls.HotSpot.HScircle;

import java.awt.RenderingHints;

import processing.core.PApplet;
import processing.core.PGraphicsJava2D;
import processing.event.MouseEvent;

/**
* This control simulates a digital joystick and is designed to give more
* intuitive control in game scenarios where you might use the keyboard
* e.g. WASD keys for movement. <br>
*
* The joystick has two modes - in the default mode the joystick just
* responds to movement in 4 directions (left,right, up and down) the
* second mode allows for diagonals so recognises 8 directions. <br>
*
* As in a real joystick you have a dead zone near the centre which
* does not generate signals, thus avoiding jitter. This area is shown
* graphically. <br>
*
* The direction of the joystick is represented by an integer in the
* range 0-7 and -1 when in the dead zone. <br>
* <pre>
*     5   6   7
*     \  |  /
*       \ | /
*   4 --- + --- 0         + is the dea zone so -1
*       / | \
*      /  |  \
*     3   2   1
* </pre> <br>
* As well as the direction there are two useful methods to decode these
* into X and Y directions - <pre>getStickX()</pre> and <pre>getStickY()</pre>
* which give the values. <br>
* <pre>
* X= -1   0   +1
*     \  |  /  -1
*       \ | /
*     --- + ---  0
*       / | \
*      /  |  \
*           Y=  +1
* </pre> <br>
* The stick will auto-center when released. <br>
*
* The minimum size for this control is 40x40 pixels and this is enforced
* when the control is created. If necessary the width and/or height the
* rectangle will be increased to 40pixels.
*
* @author Peter Lager
*
*/
public class GStick extends GAbstractControl {
  // palette index constants
  protected static final int BORDERS = 0;
  protected static final int LED_INACTIVE = 1;
  protected static final int LED_ACTIVE = 14;
  protected static final int STICK = 0;
  protected static final int STICK_TOP = 3;
  protected static final int STICK_TOP_OVER = 11;
  protected static final int STICK_TOP_PRESS = 14;
  protected static final int STICK_TOP_DRAG = 15;
  protected static final int OUTERRING = 6;
  protected static final int ACTIONRING = 5;
  protected static final int BACK = 6;
  protected static final int ROD = 1;
  //angle constants
  protected static final float RAD90 = PApplet.radians(90);
  protected static final float RAD45 = PApplet.radians(45);
  protected static final float RAD22_5 = PApplet.radians(22.5f);


  protected static final int[] posMap = new int[] { 0x01, 0x07, 0x04, 0x1c, 0x10, 0x70, 0x40, 0xc1 };
  protected static final int[] posX = new int[] {  110, -1, -1, -101 };
  protected static final int[] posY= new int[] {  01110, -1, -1, -1 };
 

  protected final float ledWidth, ledHeight;
  protected float ledRingRad;

  protected float actionRad, actionRadLimit, gripRad, rodRad;
  protected float rodLength = 0, stickAngle;
 
  protected int position = -1;
  protected int mode = X4; // X4 or X8
  protected int status = OFF_CONTROL;

  /**
   * Create the stick inside the specified rectangle.
   * @param theApplet
   * @param p0
   * @param p1
   * @param p2
   * @param p3
   */
  public GStick(PApplet theApplet, float p0, float p1, float p2, float p3) {
    super(theApplet, p0, p1, p2, p3);
    // Enforce minimum size constraint
    if(width < 40 || height < 40)
      resize(PApplet.max(width,40), PApplet.max(height,40));

    buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D);
    buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    buffer.rectMode(PApplet.CORNER);
    buffer.ellipseMode(PApplet.CORNER);
    opaque = false;
    // Calculate stick metrics
    float stickSize = PApplet.min(width, height);
    float mag = stickSize/50;
    ledWidth = 6 * mag;
    ledHeight = 1.6f * ledWidth;
    ledRingRad = (stickSize - ledWidth - 3)/2;
    actionRad = 0.50f * ledRingRad;
    gripRad = 4.0f * mag;
    rodRad = 3.0f * mag; 
    actionRadLimit = ledRingRad - gripRad - ledWidth/2;
   
    hotspots = new HotSpot[]{
        new HScircle(1, width/2, height/2, gripRad)
    };
    z = Z_SLIPPY;

    // Now register control with applet
    createEventHandler(G4P.sketchApplet, "handleStickEvents",
        new Class<?>[]{ GStick.class, GEvent.class },
        new String[]{ "stick", "event" }
    );
    registeredMethods = DRAW_METHOD | MOUSE_METHOD ;
    cursorOver = HAND;
    G4P.addControl(this);
  }

  /**
   * Sets the stick mode to either 4 or 8 directions. <br>
   * If the mode parameter should be either G4P.X4 or G4P.X8 any
   * other value will be silently ignored
   *
   * @param m the new mode
   */
  public void setMode(int m){
    if(m != mode && m == X4 || m == X8){
      mode = m;
      bufferInvalid = true;
    }
  }
 
  /**
   * Get the current mode
   * @return
   */
  public int getMode(){
    return mode;
  }
 
  /**
   * Returns the current position of the stick based on <br>
   * <pre>
   *     5   6   7
   *     \  |  /
   *       \ | /
   *   4 --- + --- 0         + is the dea zone so -1
   *       / | \
   *      /  |  \
   *     3   2   1
   * </pre> <br>
   * @return current stick direction
   */
  public int getPosition(){
    return position;
  }
 
  /**
   * Get the X position of the stick from <br>
   *  * <pre>
   * X= -1   0   +1
   *     \  |  /  -1
   *       \ | /
   *     --- + ---  0
   *       / | \
   *      /  |  \
   *           Y=  +1
   * </pre> <br>
   * @return the X value (-1, 0 or 1)
   */
  public int getStickX(){
    return (position < 0) ? 0 : posX[position];
  }
 
  /**
   * Get the Y position of the stick from <br>
   *  * <pre>
   * X= -1   0   +1
   *     \  |  /  -1
   *       \ | /
   *     --- + ---  0
   *       / | \
   *      /  |  \
   *           Y=  +1
   * </pre> <br>
   * @return the Y value (-1, 0 or 1)
   */
  public int getStickY(){
    return (position < 0) ? 0 : posY[position];
  }

  /**
   * Calculate the angle to the knob centre making sure it is in
   * the range 0-360
   * @param px relative to centre
   * @param py relative to centre
   * @return the angle made by the stick
   */
  protected float calcStickAngle(float px, float py){
    float a = PApplet.atan2(py, px);
    if(a < 0)
      a += PApplet.TWO_PI;
    return a; 
  }
 
  /**
   * Calculate the position (mode dependent) from an angle in the range 0-2PI
   * @param a the angle 0-2PI
   * @return direction (0-7)
   */
  protected int getPositionFromAngle(float a){
    int newState;
    if(mode == 1){
      a = (a + RAD45) % PApplet.TWO_PI;
      newState = 2 * (int)(a / RAD90);
    }
    else {
      a = (a + RAD22_5) % PApplet.TWO_PI;
      newState = (int)(a / RAD45);
    }
    return newState % 8;
  }

  public void mouseEvent(MouseEvent event){
    if(!visible || !enabled || !available) return;

    calcTransformedOrigin(winApp.mouseX, winApp.mouseY);
    currSpot = whichHotSpot(ox, oy);
    // Make ox and oy relative to the centre of the stick
    ox -= width/2;
    oy -= height/2;

    // currSpot == 1 for text display area
    if(currSpot >= 0 || focusIsWith == this)
      cursorIsOver = this;
    else if(cursorIsOver == this)
      cursorIsOver = null;

    switch(event.getAction()){
    case MouseEvent.PRESS:
      if(focusIsWith != this && currSpot > -1 && z > focusObjectZ()){
        status = PRESS_CONTROL;
        position = -1;
        rodLength = PApplet.sqrt(ox*ox + oy*oy);
        stickAngle = calcStickAngle(ox, oy);
        dragging = false;
        takeFocus();
        bufferInvalid = true;
      }
      break;
    case MouseEvent.RELEASE:
      if(focusIsWith == this){
        loseFocus(null);
      }
      // If we are not already near the centre then make it so
      // and fire an event
      if(position != -1){
        position = -1;
        fireEvent(this, GEvent.CHANGED);
      }
      hotspots[0].adjust(width/2, height/2);
      rodLength = stickAngle = 0;
      dragging = false;
      status = OFF_CONTROL;
      bufferInvalid = true;
      break;
    case MouseEvent.DRAG:
      if(focusIsWith == this){
        status = DRAG_CONTROL;
        dragging = true;
        rodLength = PApplet.sqrt(ox*ox + oy*oy);
        stickAngle = calcStickAngle(ox, oy);
        int newPosition = -1;
        if(rodLength >= actionRad){
          newPosition = getPositionFromAngle(stickAngle);
        }
        if(rodLength > actionRadLimit){
          ox = actionRadLimit * PApplet.cos(stickAngle);
          oy = actionRadLimit * PApplet.sin(stickAngle);
          rodLength = actionRadLimit;
        }
        hotspots[0].adjust(ox + width/2, oy + height/2)
        if(newPosition != position){
          position = newPosition;
          fireEvent(this, GEvent.CHANGED);
        }
        bufferInvalid = true;
      }
      break;
    case MouseEvent.MOVE:
      int currStatus = status;
      // If dragged state will stay as PRESSED
      if(currSpot == 1)
        status = OVER_CONTROL;
      else
        status = OFF_CONTROL;
      if(currStatus != status)
        bufferInvalid = true;
      break;     
    }
  }
 
  public void draw(){
    if(!visible) return;

    // Update buffer if invalid
    updateBuffer();
    winApp.pushStyle();

    winApp.pushMatrix();
    // Perform the rotation
    winApp.translate(cx, cy);
    winApp.rotate(rotAngle);
    // Move matrix to line up with top-left corner
    winApp.translate(-halfWidth, -halfHeight);
    // Draw buffer
    winApp.imageMode(PApplet.CORNER);
    if(alphaLevel < 255)
      winApp.tint(TINT_FOR_ALPHA, alphaLevel);
    winApp.image(buffer, 0, 0)
    winApp.popMatrix();

    winApp.popStyle();
  }

  protected void updateBuffer(){
    if(bufferInvalid) {
      bufferInvalid = false;
      buffer.beginDraw();
      // Back ground colour
      if(opaque == true)
        buffer.background(palette[BACK]);
      else
        buffer.background(buffer.color(255,0));
      // Move origin to centre

      buffer.translate(width/2, height/2);

      buffer.fill(palette[OUTERRING]);
      buffer.stroke(palette[BORDERS]);
      buffer.strokeWeight(1.0f);
      buffer.ellipse(0,0,2*ledRingRad, 2*ledRingRad);
      buffer.ellipse(0,0,2*actionRad, 2*actionRad);
      // Draw everything except the stick
      buffer.pushMatrix();
      int led = 0x00000001, delta = 2/mode;
      for(int i = 0; i < 8; i += delta){
        buffer.stroke(palette[BORDERS]);
        buffer.strokeWeight(1.0f);
        buffer.line(0,0,ledRingRad,0);
        // Only draw LEDs on even directions
        if(i%2 == 0){
          buffer.noStroke();
          if(position >= 0 && (posMap[position] & led) == led)
            buffer.fill(palette[LED_ACTIVE]);
          else
            buffer.fill(palette[LED_INACTIVE]);
          buffer.ellipse(ledRingRad,0,ledWidth,ledHeight);
        }
        led <<= delta;
        buffer.rotate(delta * RAD45);
      }
      buffer.popMatrix();
     
      // Draw the inactive area near the centre of the
      buffer.fill(palette[ACTIONRING]);
      buffer.stroke(palette[BORDERS]);
      buffer.strokeWeight(1.0f);
      buffer.ellipse(0,0,2*actionRad, 2*actionRad);

      // Draw the rod and button
      buffer.pushMatrix();
      buffer.rotate(stickAngle);
      buffer.noStroke();
      buffer.fill(palette[ROD]);
      buffer.ellipse(0,0,2*rodRad,2*rodRad);
      buffer.rect(0,-rodRad,rodLength,2*rodRad);
      buffer.strokeWeight(1);
      buffer.stroke(palette[ROD]);
      buffer.fill(palette[STICK_TOP_DRAG]);
      // Draw thumb
      switch(status){
      case OFF_CONTROL:
        buffer.fill(palette[STICK_TOP]);
        break;
      case OVER_CONTROL:
        buffer.fill(palette[STICK_TOP_OVER]);
        break;
      case PRESS_CONTROL:
        buffer.fill(palette[STICK_TOP_PRESS]);
        break;
      case DRAG_CONTROL:
        buffer.fill(palette[STICK_TOP_DRAG]);
        break;
      }
      buffer.ellipse(rodLength,0,2*gripRad, 2*gripRad);   
      buffer.popMatrix();
      buffer.endDraw();
   
  }
}
 
TOP

Related Classes of g4p_controls.GStick

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.